using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Matrix.Notification.Domain.Notifications;
using Matrix.Notification.Domain.Shared.Notifications;

namespace Matrix.Notification.EntityFrameworkCore.Notifications
{
    /// <summary>
    /// 通知模板仓储实现
    /// </summary>
    public class NotificationTemplateRepository : EfCoreRepository<NotificationDbContext, NotificationTemplate, Guid>, INotificationTemplateRepository
    {
        public NotificationTemplateRepository(IDbContextProvider<NotificationDbContext> dbContextProvider)
            : base(dbContextProvider)
        {
        }

        public async Task<NotificationTemplate?> GetWithDetailsAsync(
            Guid id,
            CancellationToken cancellationToken = default)
        {
            return await DbSet
                .IncludeDetails()
                .FirstOrDefaultAsync(x => x.Id == id, GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> GetListAsync(
            string? sorting = null,
            int maxResultCount = int.MaxValue,
            int skipCount = 0,
            string? filter = null,
            TemplateType? type = null,
            NotificationType? notificationType = null,
            TemplateApprovalStatus? approvalStatus = null,
            bool? isEnabled = null,
            bool? isSystem = null,
            string? category = null,
            string? language = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .WhereIf(!filter.IsNullOrWhiteSpace(), x =>
                    x.Name.Contains(filter!) ||
                    x.DisplayName.Contains(filter!) ||
                    x.Description!.Contains(filter!))
                .WhereIf(type.HasValue, x => x.Type == type.Value)
                .WhereIf(notificationType.HasValue, x => x.NotificationType == notificationType.Value)
                .WhereIf(approvalStatus.HasValue, x => x.ApprovalStatus == approvalStatus.Value)
                .WhereIf(isEnabled.HasValue, x => x.IsEnabled == isEnabled.Value)
                .WhereIf(isSystem.HasValue, x => x.IsSystem == isSystem.Value)
                .WhereIf(!category.IsNullOrWhiteSpace(), x => x.Category == category)
                .WhereIf(!language.IsNullOrWhiteSpace(), x =>
                    x.DefaultLanguage == language ||
                    x.SupportedLanguages!.Contains(language))
                .OrderBy(sorting ?? nameof(NotificationTemplate.Name))
                .PageBy(skipCount, maxResultCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<long> GetCountAsync(
            string? filter = null,
            TemplateType? type = null,
            NotificationType? notificationType = null,
            TemplateApprovalStatus? approvalStatus = null,
            bool? isEnabled = null,
            bool? isSystem = null,
            string? category = null,
            string? language = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .WhereIf(!filter.IsNullOrWhiteSpace(), x =>
                    x.Name.Contains(filter!) ||
                    x.DisplayName.Contains(filter!) ||
                    x.Description!.Contains(filter!))
                .WhereIf(type.HasValue, x => x.Type == type.Value)
                .WhereIf(notificationType.HasValue, x => x.NotificationType == notificationType.Value)
                .WhereIf(approvalStatus.HasValue, x => x.ApprovalStatus == approvalStatus.Value)
                .WhereIf(isEnabled.HasValue, x => x.IsEnabled == isEnabled.Value)
                .WhereIf(isSystem.HasValue, x => x.IsSystem == isSystem.Value)
                .WhereIf(!category.IsNullOrWhiteSpace(), x => x.Category == category)
                .WhereIf(!language.IsNullOrWhiteSpace(), x =>
                    x.DefaultLanguage == language ||
                    x.SupportedLanguages!.Contains(language))
                .LongCountAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> GetByNotificationTypeAsync(
            NotificationType notificationType,
            NotificationChannel channel,
            bool? isEnabled = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.NotificationType == notificationType)
                .Where(x => x.SupportsChannel(channel))
                .WhereIf(isEnabled.HasValue, x => x.IsEnabled == isEnabled.Value)
                .Where(x => x.ApprovalStatus == TemplateApprovalStatus.Approved || x.ApprovalStatus == TemplateApprovalStatus.NotRequired)
                .OrderByDescending(x => x.Priority)
                .ThenBy(x => x.UsageCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> GetByTypeAsync(
            TemplateType type,
            bool? isEnabled = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.Type == type)
                .WhereIf(isEnabled.HasValue, x => x.IsEnabled == isEnabled.Value)
                .OrderByDescending(x => x.Rating)
                .ThenByDescending(x => x.UsageCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> GetPopularTemplatesAsync(
            int maxCount = 20,
            TemplateType? type = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .WhereIf(type.HasValue, x => x.Type == type.Value)
                .Where(x => x.IsEnabled)
                .Where(x => x.UsageCount > 0)
                .OrderByDescending(x => x.UsageCount)
                .ThenByDescending(x => x.Rating)
                .Take(maxCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> GetTopRatedTemplatesAsync(
            int maxCount = 20,
            TemplateType? type = null,
            int minRatingCount = 5,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .WhereIf(type.HasValue, x => x.Type == type.Value)
                .Where(x => x.IsEnabled)
                .Where(x => x.RatingCount >= minRatingCount)
                .OrderByDescending(x => x.Rating)
                .ThenByDescending(x => x.RatingCount)
                .Take(maxCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> GetPendingApprovalTemplatesAsync(
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.ApprovalStatus == TemplateApprovalStatus.Pending)
                .Where(x => !x.IsSystem)
                .OrderBy(x => x.CreationTime)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> GetByCategoryAsync(
            string category,
            bool? isEnabled = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.Category == category)
                .WhereIf(isEnabled.HasValue, x => x.IsEnabled == isEnabled.Value)
                .OrderByDescending(x => x.Priority)
                .ThenBy(x => x.Name)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> GetSystemTemplatesAsync(
            TemplateType? type = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.IsSystem)
                .WhereIf(type.HasValue, x => x.Type == type.Value)
                .OrderBy(x => x.Type)
                .ThenBy(x => x.Name)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> SearchAsync(
            string keyword,
            TemplateType? type = null,
            NotificationType? notificationType = null,
            int maxCount = 50,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.Name.Contains(keyword) ||
                           x.DisplayName.Contains(keyword) ||
                           x.Description!.Contains(keyword) ||
                           x.Tags.Contains(keyword))
                .WhereIf(type.HasValue, x => x.Type == type.Value)
                .WhereIf(notificationType.HasValue, x => x.NotificationType == notificationType.Value)
                .Where(x => x.IsEnabled)
                .OrderByDescending(x => x.Priority)
                .ThenByDescending(x => x.UsageCount)
                .Take(maxCount)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task<bool> IsNameExistsAsync(
            string name,
            Guid? excludeId = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .WhereIf(excludeId.HasValue, x => x.Id != excludeId.Value)
                .AnyAsync(x => x.Name == name, GetCancellationToken(cancellationToken));
        }

        public async Task<bool> IsTemplateCodeExistsAsync(
            string templateCode,
            Guid? excludeId = null,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .WhereIf(excludeId.HasValue, x => x.Id != excludeId.Value)
                .AnyAsync(x => x.TemplateCode == templateCode, GetCancellationToken(cancellationToken));
        }

        public async Task<NotificationTemplate?> GetByTemplateCodeAsync(
            string templateCode,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .FirstOrDefaultAsync(x => x.TemplateCode == templateCode, GetCancellationToken(cancellationToken));
        }

        public async Task<List<NotificationTemplate>> GetByParentTemplateAsync(
            Guid parentTemplateId,
            CancellationToken cancellationToken = default)
        {
            return await (await GetQueryableAsync())
                .IncludeDetails()
                .Where(x => x.ParentTemplateId == parentTemplateId)
                .OrderBy(x => x.Name)
                .ToListAsync(GetCancellationToken(cancellationToken));
        }

        public async Task UpdateUsageStatisticsAsync(
            Guid templateId,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();
            var template = await DbSet.FindAsync(new object[] { templateId }, GetCancellationToken(cancellationToken));

            if (template != null)
            {
                template.RecordUsage();
                await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
            }
        }

        public async Task UpdateRatingAsync(
            Guid templateId,
            double rating,
            CancellationToken cancellationToken = default)
        {
            var dbContext = await GetDbContextAsync();
            var template = await DbSet.FindAsync(new object[] { templateId }, GetCancellationToken(cancellationToken));

            if (template != null)
            {
                template.SetRating(rating);
                await dbContext.SaveChangesAsync(GetCancellationToken(cancellationToken));
            }
        }
    }
}